﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace WDA.Core
{
   /// <summary>
   /// Provides means to access useful invocation context information.
   /// </summary>
   public static class MethodInvocationExtensions
   {
      #region Const
      /// <summary>
      /// Name of WDAB service which invocation was intercepted.
      /// </summary>
      public const string ServiceName = "F4050F47-98A9-4a40-AEAC-AD29DECD0470";
      /// <summary>
      /// Contract type of WDAB service which invocation was intercepted.
      /// </summary>
      public const string ContractType = "0645CEAC-1C4F-40f9-A28D-9B6E697F0D32";
      /// <summary>
      /// Implementation type of WDAB service which invocation was intercepted.
      /// If interception was done on client side, implementation type is set to null.
      /// </summary>
      public const string ImplementationType = "0C75AC03-B114-4df9-AB3A-6E0A9D83F17A";
      #endregion

      /// <summary>
      /// Returns name of WDAB service which invocation was intercepted.
      /// </summary>
      /// <param name="invocation"></param>
      /// <returns>Name of WDAB service which invocation was intercepted.</returns>
      public static string GetServiceName(this IMethodInvocation invocation)
      {
         return (string)invocation.InvocationContext[ServiceName];
      }
      /// <summary>
      /// Returns contract type of WDAB service which invocation was intercepted.
      /// </summary>
      /// <param name="invocation"></param>
      /// <returns>Contract type of WDAB service which invocation was intercepted.</returns>
      public static Type GetContractType(this IMethodInvocation invocation)
      {
         return (Type)invocation.InvocationContext[ContractType];
      }
      /// <summary>
      /// Returns implementation type of WDAB service which invocation was intercepted.
      /// If interception was done on client side, implementation type is set to null.
      /// </summary>
      /// <param name="invocation"></param>
      /// <returns>Implementation type of WDAB service which invocation was intercepted.</returns>
      public static Type GetImplementationType(this IMethodInvocation invocation)
      {
         return (Type)invocation.InvocationContext[ImplementationType];
      }
   }

   /// <summary>
   /// Sets useful context values for other <see cref="ICallHandler"/>s in chain. 
   /// </summary>
   internal class InterceptionContextSetterCallHandler : ICallHandler
   {      
      private readonly string _serviceName;
      private readonly Type _contractType;
      private readonly Type _implementationType;

      /// <summary>
      /// Creates new context setter instance.
      /// </summary>
      /// <param name="serviceName">Name of WDAB service.</param>
      /// <param name="contractType">Contract type.</param>
      /// <param name="implementationType">Implementation type (if server-side).</param>
      public InterceptionContextSetterCallHandler(string serviceName, Type contractType, Type implementationType)
      {
         _serviceName = serviceName;
         _contractType = contractType;
         _implementationType = implementationType;
      }

      #region ICallHandler Members
      public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
      {
         input.InvocationContext.Add(MethodInvocationExtensions.ServiceName, _serviceName);
         input.InvocationContext.Add(MethodInvocationExtensions.ContractType, _contractType);
         input.InvocationContext.Add(MethodInvocationExtensions.ImplementationType, _implementationType);
         return getNext()(input, getNext);
      }      
      /// <summary>
      /// Returns 1 as order value meaning that this interceptor should be first in chain. Trying to set this
      /// value causes exception.
      /// </summary>
      public int Order
      {
         get
         {
            return 1;
         }
         set
         {
            throw new NotSupportedException();
         }
      }
      #endregion
   }
}
